Skip to content

refactor(datagrid): interaction fixes, window UndoManager, multi-cell paste#924

Merged
datlechin merged 8 commits intomainfrom
refactor/datagrid-phase3
Apr 28, 2026
Merged

refactor(datagrid): interaction fixes, window UndoManager, multi-cell paste#924
datlechin merged 8 commits intomainfrom
refactor/datagrid-phase3

Conversation

@datlechin
Copy link
Copy Markdown
Member

Summary

DataGrid audit Phase 3 — interaction fixes, clipboard improvements, and UndoManager unification.

# Issue Resolution
#10 Escape clears selection outside edit cancelOperation: is now a no-op when not editing.
#11 Right-click during edit shows row menu Removed rightMouseDown/menu(for:) overrides from DataGridFieldEditor. Native text context menu fires during editing.
#12 Ctrl+H/J/K/L override Deleted the if modifiers.contains(.control) block in keyDown.
#24 NSUndoManager bypassed DataChangeManager uses a provider for the window's UndoManager instead of a private instance. Removed undo:/redo: selectors from KeyHandlingTableView so the responder chain dispatches Cmd+Z to the window's UndoManager. clearChangesAndUndoHistory uses removeAllActions(withTarget:) to avoid wiping the editor's undo stack.
#32 Copy writes plain text only ClipboardService.writeTabular writes .string (TSV), public.utf8-tab-separated-values-text, and .html (HTML table). New HtmlTableEncoder handles entity escaping.
#33 No multi-cell paste pasteCellsFromClipboard(anchorRow:anchorColumn:) parses clipboard as TSV grid, applies cell-by-cell from the focused cell, wraps in beginUndoGrouping/endUndoGrouping for a single Cmd+Z. Falls through to row-append paste when the clipboard isn't a multi-cell grid or no cell is focused.
#35 No Shift+Tab backward Tab handler checks event.modifierFlags.contains(.shift) and calls handleShiftTabKey() which walks to the previous cell, wrapping rows.
#36 Row drag uses private type only pasteboardWriterForRow adds .string (TSV) and .html alongside the internal rowDragType.

Phase 1 landed in #919, Phase 2 in #921.

Test plan

  • Escape while not editing: focus stays, no deselection
  • Escape while editing a cell: editing ends, value reverts, focus stays
  • Right-click while editing: native text menu (Cut/Copy/Paste) appears, not row menu
  • Ctrl+H/J/K/L no longer navigates the grid
  • Cmd+Z after editing a cell: cell reverts (uses window UndoManager)
  • Cmd+Z in the SQL editor, then switch to data grid and Cmd+Z: each undoes its own actions
  • Edit > Undo from the menu bar works for both editor and data grid
  • Copy rows and paste into Excel/Numbers/Google Sheets: columns and rows preserved
  • Copy a 3x3 block from Excel, focus a cell in the grid, Cmd+V: cells fill from anchor
  • Multi-cell paste then Cmd+Z: all pasted cells revert in one step
  • Paste with no focused cell: falls through to row-append paste
  • Shift+Tab moves focus to the previous cell, wrapping to the previous row
  • Tab still moves forward
  • Drag a row to another app (e.g. TextEdit): row data appears as text
  • Drag a row within the grid: row reorder still works

Files

  • KeyHandlingTableView.swift — removed Ctrl+HJKL, undo:/redo:, Escape behavior; added Shift+Tab; updated paste to try cell-paste first
  • CellTextField.swift — removed rightMouseDown/menu(for:) from DataGridFieldEditor
  • DataChangeManager.swiftundoManagerProvider, onUndoApplied callback, registerUndo helper, target-scoped removeAllActions
  • DataGridCoordinator.swift — removed canUndo/canRedo
  • MainContentCoordinator.swift — wires undoManagerProvider and onUndoApplied
  • MainContentCoordinator+RowOperations.swifthandleUndoResult replaces undoLastChange/redoLastChange
  • MainContentCommandActions.swift — undo/redo delegates to window UndoManager
  • DataTabGridDelegate.swiftdataGridUndo/dataGridRedo are now no-ops
  • RowOperationsManager.swiftapplyUndoResult made internal
  • ClipboardService.swiftwriteTabular(tsv:html:) method
  • HtmlTableEncoder.swift (new) — HTML table encoding with entity escaping
  • DataGridView+RowActions.swiftcopyRows/copyRowsWithHeaders use writeTabular; drag adds TSV+HTML
  • DataGridView+CellPaste.swift (new) — pasteCellsFromClipboard with undo grouping

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

https://github.com/TableProApp/TablePro/blob/c3a0e67c382c58f09c819f9740fb16b82d2eb2d6/Views/Results/Extensions/DataGridView+CellPaste.swift#L13-L15
P2 Badge Handle CRLF when parsing pasted grid

pasteCellsFromClipboard splits only on "\n", so clipboard payloads that use \r\n (common from spreadsheets and browser tables) keep a trailing \r in each last cell value. That \r is then committed to the table, producing hard-to-see corrupted values (e.g., mismatched filters/equality checks after paste). Parse using newline-aware splitting (or trim \r) before commitCellEdit.


https://github.com/TableProApp/TablePro/blob/c3a0e67c382c58f09c819f9740fb16b82d2eb2d6/Views/Results/Extensions/DataGridView+CellPaste.swift#L19
P2 Badge Bound multi-cell paste by the widest parsed row

maxCol is derived from grid.first?.count, which truncates paste when later rows contain more columns than the first row. In irregular TSV input (for example, first line with trimmed trailing empties), valid cells in subsequent rows are silently dropped. Compute bounds per-row during iteration or use the maximum row width across grid.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@datlechin datlechin merged commit a8d2993 into main Apr 28, 2026
2 checks passed
@datlechin datlechin deleted the refactor/datagrid-phase3 branch April 28, 2026 07:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant